#include "console.h"

/* ancora ci sono dei fix a giro da fare, ogni tanto c'e'
 * qualche access violation */

Console::Console(void)
{
	buffer = (char**)malloc(sizeof(char*)*CONSOLE_ROWS);
	memset(buffer, 0x00, sizeof(char*)*CONSOLE_ROWS);
	
	curInput = (char *)malloc(50);
	memset(curInput, 0x00, 50);
	
	/* 20 comandi massimi memorizzati */
	inputBuffer = (char**)malloc(sizeof(char*)*20);
	memset(inputBuffer, 0x00, sizeof(char*)*20);
	
	m_rows = 0;
	m_inputRows = 0;
	m_lastLine = -1;
}

Console::~Console(void)
{
	
}

bool Console::execLine(char *line)
{
	/* spezziamo la stringa immessa in token */
	/* TODO: in realta' quando verra' scritta roba senza comandi
	 * specifici andra' interpretata come say e detta in chat
	 * (se connessi ad un server) */
	
	bool found = false;
	char *tokens[5];
	memset(tokens, 0x00, sizeof(char*)*5);
	short curToken = 0;
	
	tokens[curToken] = strtok(line, " ");
	
	while (tokens[curToken] != '\0')
	{
		++curToken;
		tokens[curToken] = strtok('\0', " ");
	}
	
	if (curToken == 1)
	{
		if (strcmp(tokens[0], "help") == 0)
		{
			appendLine("This is the help command, try ^2help parameter ^0to get help for a specific topic or");
			appendLine("command. Parameter can be also an argument, try for example ^2help colors^0.");
			
			found = true;
		}
		else if (strcmp(tokens[0], "exit") == 0)
		{
			exit(0);
		}
	}
	else if (curToken == 2)
	{
		if (strcmp(tokens[0], "help") == 0 && strcmp(tokens[1], "colors") == 0)
		{
			appendLine("You can use colors by using the escape character ^^. Supported colors are the following:");
			appendLine("0 - white, ^11 - red, ^22 - green, ^33 - blue, ^44 - yellow, ^55 - magenta, ^66 - cyan");
			appendLine("^77 - orange, ^88 - purple, ^99 - black");
			
			found = true;
		}
	}
	else if (curToken == 3)
	{
		/* set variabile */
		if (strcmp(tokens[0], "set") == 0)
		{		
			bool foundVar = false;
			
			/* scorre tra i nomi di variabili e cerca quella giusta */
			for (int i = 0; i < VARIABLES_NUM; ++i)
			{
				if (strcmp(tokens[1], varNames[i]) == 0)
				{
					foundVar = true;
					
					/* in base al tipo usiamo il metodo apposito */
					switch (varTypes[i])
					{
						case BOOL_VAR:
						{							
							if (strcmp(tokens[2], "true") == 0)
							{
								variables->setVar(tokens[1], true);
								appendLine("^2Variable Set.");
							}
							else if (strcmp(tokens[2], "false") == 0)
							{
								variables->setVar(tokens[1], false);
								appendLine("^2Variable Set.");
							}
							else
								appendLine("^1Incorrect format, expected: BOOL (true, false)");
							
							found = true;
							
							break;
						}
						case FLOAT_VAR:
						{
							
						}
						case INT_VAR:
						{
							int height;
							
							/* il numero e' giusto */
							if ((height = atoi(tokens[2])) != 0)
							{
								variables->setVar(tokens[1], height);
							}	
							else
								appendLine("^1Incorrect format, expected: INTEGER (number)");
						}
						case STRING_VAR:
						{
							
						}
						
						/* TODO: finire altri casi */
					}
				
					break;
				}
			}
			
			if (!foundVar)
			{
				appendLine("^1Variable name used not exists.");
				found = true;
			}
		}
		else if (strcmp(tokens[0], "get") == 0)
		{		
			/* scorre tra i nomi di variabili e cerca quella giusta */
			for (int i = 0; i < VARIABLES_NUM; ++i)
			{
				if (strcmp(tokens[1], varNames[i]) == 0)
				{
					/* in base al tipo usiamo il metodo apposito */
					switch (varTypes[i])
					{
						case BOOL_VAR:
						{

							char *string = new char[100];
							sprintf(string, "Variable ^2%s: %s", tokens[1], (variables->getBoolVar(tokens[1]))?("true"):("false"));
							appendLine(string);
							
							found = true;
							
							break;
						}
						case FLOAT_VAR:
						{
							
						}
						case INT_VAR:
						{
							
						}
						case STRING_VAR:
						{
							
						}
						
						/* TODO: finire altri casi */
					}
				
					break;
				}
			}
		}
	}
	
	if (!found)
	{
		appendLine("^1Syntax error.");
	}
	
	return true;
}

void Console::showHints(void)
{
	/* ora funziona solo sui comandi (prima stringa dell'input) */

	char *token = new char[100];
	memset(token, 0x00, 100);
	char *bestHint = new char[100];
	memset(bestHint, 0x00, 100);
	
	if (strlen(curInput) > 0)
	{
		token = strtok(strdup(curInput), " ");
		
		/* se la stringa inizia con set o get */
		if (strcmp(token, "set") == 0 || strcmp(token, "get") == 0)
		{
			char *cmd = strdup(token);
			char *hints = new char[100];
			memset(hints,0x00,100);
			delete token;
			token = strtok('\0', " ");
			
			if (token == '\0')
				return;
			
			bool found = true;
			bool anyone = false;
			short onlyOne = -1;
			
			for (int i = 0; i < VARIABLES_NUM; ++i)
			{
				for (unsigned int j = 0; j < strlen(token); ++j)
				{
					if (token[j] != varNames[i][j])
						found = false;
				}
			
				if (found)
				{
					if (bestHint[0] == 0)
						strcpy(bestHint, varNames[i]);
					else
					{
						for (int w = 0; true; ++w)
						{
							if (bestHint[w] != varNames[i][w])
							{
								for (unsigned int k = w; k < strlen(bestHint); ++k)
									bestHint[k] = '\0';
								
								break;
							}
						}
					}
					
					
					if (!anyone)
					{
						onlyOne = i; 
						strcat(hints, "Hints: ^2");
					}
					else
					{
						onlyOne = -1;
						strcat(hints, "^0, ^2");
					}
		
					anyone = true;
					strcat(hints, varNames[i]);		
				}
				
				found = true;
			}
			
			if (onlyOne != -1)
			{
				delete curInput;
				
				curInput = new char[100];
				memset(curInput, 0x00, 100);
				strcat(curInput, cmd);
				strcat(curInput, " ");
				strcat(curInput, varNames[onlyOne]);
				strcat(curInput, " ");
			}
			else
			{
				if (anyone)
				{
					appendLine(strdup(hints));
					delete curInput;
					curInput = new char[100];
					memset(curInput, 0x00, 100);
					strcat(curInput, cmd);
					strcat(curInput, " ");
					strcat(curInput, bestHint);
					//curInput = strdup(bestHint);
				}
			}
			
			delete hints;
			hints = '\0';
			delete token;
			token = '\0';
			delete bestHint;
			bestHint = '\0';
		}
		else
		{
			/* TODO: aggiungere bestHint pure qui */
			char *hints = new char[100];
			memset(hints,0x00,100);
			
			bool found = true;
			bool anyone = false;
			short onlyOne = -1;
			
			for (int i = 0; i < COMMANDS_NUM; ++i)
			{
				for (unsigned int j = 0; j < strlen(curInput); ++j)
				{
					if (curInput[j] != commands[i][j])
						found = false;
				}
			
				if (found)
				{

					if (bestHint[0] == 0)
						strcpy(bestHint, commands[i]);
					else
					{
						for (int w = 0; true; ++w)
						{
							if (bestHint[w] != commands[i][w])
							{
								for (unsigned int k = w; k < strlen(bestHint); ++k)
									bestHint[k] = '\0';
								
								break;
							}
						}
					}					
					
					if (!anyone)
					{
						onlyOne = i; 
						strcat(hints, "Hints: ^2");
					}
					else
					{
						onlyOne = -1;
						strcat(hints, "^0, ^2");
					}
		
					anyone = true;
					strcat(hints, commands[i]);		
				}
				
				found = true;
			}
			
			if (onlyOne != -1)
			{
				delete curInput;
				
				curInput = strdup(commands[onlyOne]);
			}
			else
			{
				if (anyone)
				{
					appendLine(strdup(hints));
					delete curInput;
					curInput = new char[100];
					memset(curInput, 0x00, 100);
					strcat(curInput, bestHint);
				}
			}
			
			delete hints;
			hints = '\0';
		}
	}
}

void Console::nextUndoLine(void)
{	
	if (m_lastLine < m_inputRows - 1)
	{
		++m_lastLine;
		delete curInput;
		curInput = strdup(inputBuffer[m_lastLine]);
	}
}

void Console::prevUndoLine(void)
{
	if (m_lastLine > 0)
	{
		--m_lastLine;
		delete curInput;
		curInput = strdup(inputBuffer[m_lastLine]);
	}
	else
	{
		delete curInput;
		curInput = (char*)malloc(50);
		memset(curInput, 0x00, 50);
		m_lastLine = -1;
	}
}

void Console::appendLine(char *line)
{	
	/* shiftiamo tutto di una a destra e inseriamo la stringa in cima */

	for (int i = m_rows; i > 0; --i)
	{
		buffer[i] = buffer[i - 1];
	}
	
	buffer[0] = line;
	
	if (m_rows < CONSOLE_ROWS)
		++m_rows;
}

int Console::getRows(void)
{
	return m_rows;
}

char *Console::getRow(int i)
{
	return buffer[i];
}

char *Console::getCurInput()
{
	return curInput;
}

void Console::appendChar(char c)
{
	if (c == (char)GLFW_KEY_ENTER)
	{
		if (m_inputRows == 0 || (m_inputRows > 0 && strcmp(inputBuffer[0], curInput) != 0))
		{
			
			for (int i = m_inputRows; i > 0; --i)
				inputBuffer[i] = inputBuffer[i-1];
			
			inputBuffer[0] = strdup(curInput);
			
			if (m_inputRows < 20)
				++m_inputRows;

		}
		
		m_lastLine = -1;
		
		execLine(curInput);
		
		/* svuota la linea di input */
		memset(curInput, 0x00, 50);
	}
	else if (strlen(curInput) < CONSOLE_COLUMNS)
	{
		curInput[strlen(curInput)] = c;
	}
}

void Console::deleteChar(void)
{
	if (strlen(curInput) > 0)
		curInput[strlen(curInput)-1] = '\0';
}
